﻿using System;
using System.Collections.Generic;
using System.Xml;
using System.Web;
using System.Web.Caching;

namespace Xpus.Web.Common
{
	/// <summary>
	/// 为系统提供读取语言文件的功能。
	/// </summary>
	public class XpusCulture
	{
		/// <summary>
		/// 加载一个语言文件。
		/// </summary>
		/// <param name="virtualPath">语言文件的虚拟路径。</param>
		/// <returns>于指定文件对应的语言资源。</returns>
		public static XpusCulture LoadCulture(string virtualPath)
		{
			try
			{
				XpusCulture culture = XpusAppDataCache<XpusCulture>.Get(virtualPath);
				if (culture == null)
				{
					string path = HttpContext.Current.Server.MapPath(virtualPath);
					culture = LoadFromFile(path);
					XpusAppDataCache<XpusCulture>.Set(virtualPath, culture);
				}

				return culture;
			}
			catch (Exception ex)
			{
				throw new XpusInvalidCultureFileException(virtualPath, ex);
			}
		}

		/// <summary>
		/// 从语言资源中获取具有指定键值的字符串。
		/// </summary>
		/// <param name="key">键值。</param>
		/// <returns>具有指定键值的字符串。</returns>
		public string GetString(string key)
		{
			if (key == null)
				throw new ArgumentNullException("key");

			if (!this.StringTable.ContainsKey(key))
				throw new XpusInvalidCultureKeyException(key);

			return this.StringTable[key];
		}

		/// <summary>
		/// 判断具有指定键值的字符串是否存在。
		/// </summary>
		/// <param name="key">键值。</param>
		/// <returns>如果指定键值的字符串存在，返回true；否则返回false。</returns>
		public bool ContainsKey(string key)
		{
			return StringTable.ContainsKey(key);
		}

		/// <summary>
		/// 似有默认构造器，防止直接实例化。
		/// </summary>
		private XpusCulture()
		{
		}

		/// <summary>
		/// 用于存放语言资源中的各个字符串。
		/// </summary>
		private Dictionary<string, string> StringTable = new Dictionary<string, string>();

		/// <summary>
		/// 从文件中读取语言资源。
		/// </summary>
		/// <param name="virtualPath">语言资源文件的绝对路径。</param>
		/// <returns>从文件中读取的语言资源。</returns>
		private static XpusCulture LoadFromFile(string path)
		{
			XpusCulture culture = new XpusCulture();
			XmlDocument xdoc = new XmlDocument();
			xdoc.Load(path);

			XmlNodeList xnode = xdoc.SelectNodes("/strings/string");
			foreach (XmlNode n in xnode)
			{
				string key = n.Attributes["key"].Value;
				string value = n.InnerText;

				culture.StringTable.Add(key, value);
			}

			return culture;
		}
	}

	/// <summary>
	/// 表示资源文件不存在或无法打开等错误。
	/// </summary>
	public class XpusInvalidCultureFileException : Exception
	{
		/// <summary>
		/// 获取资源文件的虚拟路径。
		/// </summary>
		public string VirtualPath { get; private set; }

		/// <summary>
		/// 构造异常的新实例。
		/// </summary>
		/// <param name="virtualPath">语言资源文件的虚拟路径。</param>
		public XpusInvalidCultureFileException(string virtualPath)
			: this(virtualPath, null)
		{
		}

		/// <summary>
		/// 构造异常的新实例。
		/// </summary>
		/// <param name="virtualPath">语言资源文件的虚拟路径。</param>
		/// <param name="innerException">引发该异常的其它异常。</param>
		public XpusInvalidCultureFileException(string virtualPath, Exception innerException)
			: base(string.Format("无效的语言资源文件：{0}。", virtualPath), innerException)
		{
			this.VirtualPath = virtualPath;
		}
	}

	/// <summary>
	/// 表示语言资源中不存在给定的键值。
	/// </summary>
	public class XpusInvalidCultureKeyException : Exception
	{
		/// <summary>
		/// 获取指定的键值。
		/// </summary>
		public string Key { get; private set; }

		public XpusInvalidCultureKeyException(string key)
			: this(key, null)
		{
		}

		public XpusInvalidCultureKeyException(string key, Exception innerException)
			: base(string.Format("无效的语言资源键值：{0}。", key), innerException)
		{
			this.Key = key;
		}
	}

	/// <summary>
	/// 表示语言不受支持。
	/// </summary>
	public class XpusCultureNotSupportedException : Exception
	{
		/// <summary>
		/// 获取请求的语言名字。
		/// </summary>
		public string CultureName { get; private set; }

		public XpusCultureNotSupportedException(string cultureName)
			: base(string.Format("语言资源不受支持：{0}。", cultureName))
		{
			this.CultureName = cultureName;
		}
	}
}
